package com.wyw.redislock.learning.controller;

import com.wyw.redislock.learning.common.bean.Person;
import com.wyw.redislock.learning.common.bean.Response;
import com.wyw.redislock.learning.common.config.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @Title   Redis分布式锁
 * @Description
 * @Author Mr Wu yewen.wu.china@gmail.com
 * @Date 2024/3/29 9:47
 * Update History:
 * Author        Time            Content
 */
@RestController
@Slf4j
public class DemoController {
    @Autowired
    private RedisTemplate redisTemplate;
    @Resource
    private RedissonClient redisson;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    /**
     * @author: Mr Wu    yewen.wu.china@gmail.com
     * @date: 2020/7/29 17:15
     * @return
     */
    @GetMapping("redisLock")
    public String redisLock() {
        log.info("我进入了方法！");
        try (RedisLock redisLock = new RedisLock(redisTemplate, "redisKey", 30)) {//这样写就可以利用JDK1.7自动关闭
            if (redisLock.getLock()) {
                log.info("获取了redis锁！！");
                Thread.sleep(15000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        log.info("方法执行完成！！");
        return "方法执行完成";
    }

    /**
     * @author: Mr Wu    yewen.wu.china@gmail.com
     * @date: 2020/7/29 17:15
     * @return
     */
    @GetMapping("redissonLock")
    public String redissonLock() {
        /**
         * 1.获取当前时间戳
         * 2.client尝试按照顺序使用相同的key,value获取所有redis服务的锁，在获取锁的过程中的获取时间比锁过期时间短很多，这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。
         * 比如：TTL为5s,设置获取锁最多用1s，所以如果一秒内无法获取锁，就放弃获取这个锁，从而尝试获取下个锁
         * 3.client通过获取所有能获取的锁后的时间减去第一步的时间，这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁，才算真正的获取锁成功
         * 4.如果成功获取锁，则锁的真正有效时间是 TTL减去第三步的时间差 的时间；比如：TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);
         * 5.如果客户端由于某些原因获取锁失败，便会开始解锁所有redis实例；因为可能已经获取了小于3个锁，必须释放，否则影响其他client获取锁
         */
        RLock rLock = redisson.getLock("order");
        log.info("进入了redisson方法!!");
        try {
            rLock.lock(30, TimeUnit.SECONDS);
            log.info("获得了锁！！");

            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("释放了锁！！");
            rLock.unlock();
        }
        log.info("方法执行完成！！");
        return "方法执行完成！！";
    }

    /**
     * 根据用户设置锁
     * @author: Mr Wu    yewen.wu.china@gmail.com
     * @date: 2020/7/29 17:15
     * @param userId
     * @return
     */
    @GetMapping("/lockTime")
    public String lockTimeByUser(@RequestParam Long userId){
        RLock rLock = redisson.getLock("order" + userId);
        log.info("用户{}进入了redisson方法!!", userId);
        try {
            rLock.lock(30, TimeUnit.SECONDS);
            log.info("用户{}获得了锁！！", userId);

            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            log.info("用户{}释放了锁！！", userId);
            rLock.unlock();
        }
        log.info("用户{}方法执行完成！！", userId);
        return "方法执行完成！！";
    }
    @GetMapping("/test")
    public void test(){
//        String b = "A,B,C,D,E,F,G";
//        String[] splitb = b.split(",");
//        List<String> bList = Arrays.asList(splitb);
//        String collect = bList.stream().collect(Collectors.joining("\\n"));
//        String collect1 = bList.stream().collect(Collectors.joining("\\r\\n"));
//
//        redisTemplate.opsForValue().set("key1", collect);


        Person person = new Person();
        person.setId(1);
        person.setFlag(1);
        person.setTime("2020-09-10 10:01:01");

        Person person1 = new Person();
        person1.setId(2);
        person1.setFlag(1);
        person1.setTime("2020-09-10 10:01:02");

        Person person3 = new Person();
        person3.setId(3);
        person3.setFlag(1);
        person3.setTime("2020-09-10 10:01:03");

        Person person4 = new Person();
        person4.setId(3);
        person4.setFlag(0);
        person4.setTime("2020-09-10 10:01:04");
        Person person5 = new Person();
        person5.setId(1);
        person5.setFlag(0);
        person5.setTime("2020-09-10 10:01:05");
        List<Person> list = new ArrayList<>();
        list.add(person);
        list.add(person1);
        list.add(person3);
        list.add(person4);
        list.add(person5);
        redisTemplate.opsForList().rightPush("personKey", person);
        redisTemplate.opsForList().rightPush("personKey", person1);
        redisTemplate.opsForList().rightPush("personKey", person3);
        redisTemplate.opsForList().rightPush("personKey", person4);
        redisTemplate.opsForList().rightPush("personKey", person5);
        Object per = new Person();
        List<Person> a = new ArrayList();
        while((per = redisTemplate.opsForList().leftPop("personKey")) != null){
            a.add((Person) per);
            per = null;
        }
        List<Person> collect = a.stream().sorted(Comparator.comparing(Person::getTime).reversed()).collect(Collectors.toList());
        List<Person> list2 = collect.stream().filter(distinctByKey(p -> p.getId())).collect(Collectors.toList());
        List strList = new ArrayList();
        strList.add("A");
        strList.add("B");
        strList.add("C");

        List<Person> collect1 = list2.stream().map(ab -> {
            log.info(strList.get(list2.indexOf(ab)) + "");
            return ab;
        }).collect(Collectors.toList());
    }

    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        System.out.println("这个函数将应用到每一个item");
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @GetMapping("/testRedis")
    public Object testRedis(){
        String sequenceNumber = stringRedisTemplate.opsForValue().increment("testSeg").toString();
        log.info("sequenceNumber:{}", sequenceNumber);
//        Long testSeg = stringRedisTemplate.boundValueOps("testSeg").increment(1);//将redis自增长序列进行+1操作
//        log.info("testSeg:{}", testSeg);

        Response response = new Response();
//        response.setReason("失败了");
        response.setResult(true);
        response.setBusinessId("123");
        response.setBusinessId("ABC12345687");
        return response;
    }

    public Boolean releaseLock(String value) {
        // 生成key
        String s = "OBPC";
        ResourceScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("script/releaseLock.lua"));
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(scriptSource);
        redisScript.setResultType(Boolean.class);
        Boolean execute = (Boolean) redisTemplate.execute(redisScript, Collections.singletonList(s), value);
        return execute;
    }

    public Boolean existLock(){
        // 生成key 值
        String s = "OBPC";
        return null == redisTemplate.opsForValue().get(s);
    }

    @GetMapping("/hello")
    public String hello(@RequestParam String hkey, @RequestParam String value){
        redisTemplate.opsForHash().put("key1", hkey, value);
        return "success";
    }

    @GetMapping("/gethello")
    public String gethello(@RequestParam String hkey, @RequestParam String value){
        Object key1 = redisTemplate.opsForHash().get("key1", hkey);
        return key1.toString();
    }

    @GetMapping("/delhello")
    public String delhello(@RequestParam String hkey, @RequestParam String value){
        redisTemplate.opsForHash().delete("key1", hkey);
        return "success";
    }

    @GetMapping("/allhello")
    public String allhello(@RequestParam String hkey, @RequestParam String value){
        List key1 = redisTemplate.opsForHash().values("key1");
        return key1.toString();
    }

    @RequestMapping(value = "test/test.json", method = RequestMethod.GET)
    public List<Student> query(){
        Student student = new Student();
        student.setId(1);
        student.setName("1");


        Student student1 = new Student();
        student1.setId(1);
        student1.setName("1");

        List list = new ArrayList();
        list.add(student);
        list.add(student1);
        return list;
    }

    public class Student{
        private int id;
        private String name;


        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

